#include "curve.h"


float Curve::COLOUR_CONTROL_POINTS[3] = { 0.0f,0.8f,0.0f};
float Curve::COLOUR_CURVE_CONTROL[3] = {1.0f,0.5f,0.5f};
float Curve::COLOUR_CURVE[3] = {0.26f,0.26f,0.9f};

const float Curve::LINE_WIDTH = 2;
const float Curve::POINT_SIZE = 5;

using namespace std;

Curve::Curve(Plane plane,Type type,int numberPoints)
{
	m_type = type;
	m_numberPoints = numberPoints;
	m_plane = plane;

	m_showControlPoints = true;
	m_showCurveControl = true;
}


Curve::~Curve(void)
{
	m_controlPoints.DeleteAll();
	m_curvePoints.DeleteAll();
}

void Curve::ShowControlPoints(bool show)
{
	m_showControlPoints = show;
}

void Curve::ShowCurveControl(bool show)
{
	m_showCurveControl = show;
}

void Curve::Render()
{
	glDisable(GL_LIGHTING);

	glColor3f(COLOUR_CURVE[0],COLOUR_CURVE[1],COLOUR_CURVE[2]);
	glLineWidth(LINE_WIDTH);
	glBegin(GL_LINE_STRIP);
	for(unsigned int i=0; i < m_curvePoints.size(); i++)
		{
			glVertex3f(m_curvePoints[i]->x, m_curvePoints[i]->y,m_curvePoints[i]->z);
		}
	glEnd();
	glLineWidth(1);

	if (m_showControlPoints)
	{
		glColor3f(COLOUR_CONTROL_POINTS[0],COLOUR_CONTROL_POINTS[1],COLOUR_CONTROL_POINTS[2]);
		glPointSize(POINT_SIZE);
		glBegin(GL_POINTS);
		for(unsigned int i = 0; i < m_controlPoints.size();i++)
			{	
				glVertex3f(m_controlPoints[i]->x,m_controlPoints[i]->y,m_controlPoints[i]->z);
			}
		glEnd();
		glPointSize(1);
	}

	if (m_showCurveControl)
	{
		glColor3f(COLOUR_CURVE_CONTROL[0],COLOUR_CURVE_CONTROL[1],COLOUR_CURVE_CONTROL[2]);
		glBegin(GL_LINE_STRIP);
		for(unsigned int i = 0; i < m_controlPoints.size(); i++)
			{
				glVertex3f(m_controlPoints[i]->x,m_controlPoints[i]->y,m_controlPoints[i]->z);			
			}
		glEnd();
	}

	glEnable(GL_LIGHTING);
}


void Curve::Update(float dt)
{
	m_curvePoints.DeleteAll();

	calculateCurvePoints();

	glutPostRedisplay();
}

void Curve::AddControlPoint(Point3D point)
{
	m_controlPoints.push_back(new Point3D(point));
}

void Curve::AddPoint(Point3D point)
{
	m_curvePoints.push_back(new Point3D(point));
}

void Curve::DeleteAllControlPoints()
{
	m_controlPoints.DeleteAll();
	m_curvePoints.DeleteAll();
}

Point3D Curve::CubicBezierPoint(float u,const Point3D* controlPoints[])
{
	Point3D ret(0,0,0);

	float B[] = {pow(1-u, 3),3*u*pow(1-u, 2),3*pow(u, 2)*(1-u),pow(u, 3)};

	for (unsigned int i = 0; i < 4 ; i++)
	{
		ret.x += B[i]*controlPoints[i]->x;
		ret.y += B[i]*controlPoints[i]->y;
		ret.z += B[i]*controlPoints[i]->z;
	}

	return ret;
}

Point3D Curve::CubicBSplinePoint(float u,const Point3D* controlPoints[])
{
	Point3D ret(0,0,0);

	float B[] = {(1.0f/6.0f)*pow((1-u),3),
				(1.0f/6.0f)*( 4 - 6*pow(u,2) + 3*pow(u,3) ),
				(1.0f/6.0f)*( 1 + 3*u + 3*pow(u,2) - 3*pow(u,3)),
				(1.0f/6.0f)*pow(u,3)};
	
	for (unsigned int i = 0; i < 4 ; i++)
	{
		ret.x += B[i]*controlPoints[i]->x;
		ret.y += B[i]*controlPoints[i]->y;
		ret.z += B[i]*controlPoints[i]->z;
	}

	return ret;
}

void Curve::calculateCurvePoints()
{
	float step = 1.0f/(m_numberPoints-1);

	int c = m_type == BEZIER ? 3 : 1;

	// Tomo cada cuatro porque esta formado por curvas cubicas.
	for(int i = 0; i < (int)m_controlPoints.size() - 3; i+= c)
	{
		// Lista de los 4 puntos de control para calcular la spline o bezier cubica.
		const Point3D* temp[4];
		
		int j = 0;
		for (int points = i; points < i + 4 ; points++)
		{			
			temp[j] = m_controlPoints[points];
			j++;
		}
		
		unsigned int k = i == 0? 0 : 1;

		for (k; k < m_numberPoints; k++)
		{
 			Point3D point(0,0,0);
				
			if (m_type == BEZIER)
			{
				point = CubicBezierPoint(step*k,temp);
			}
			else if (m_type == BSPLINE)
			{
				point = CubicBSplinePoint(step*k,temp);
			}

			m_curvePoints.push_back(new Point3D(point));
		}

	}

}

const std::vector<Point3D*>& Curve::GetCurvePoints()const
{
	return m_curvePoints;
}

void Curve::Close()
{
	if (m_type == BSPLINE)
	{
		int it = 0;

		for(it = 0; it < 3 ; it++)
		{
			m_controlPoints.push_back(new Point3D(*(m_controlPoints[it])));
		}
	}

	Update(0);
}